#ifndef _CPU
#define _CPU
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include "store.hpp"
#include "arithmetic.hpp"
// #define debug2
// #define func

class CPU
{
    using uint = unsigned int;
    static const int queue_len = 5;
    friend struct Reorder_Buffer;
    friend struct Reservation_Station;
    friend struct Load_Store_Buffer;
private:

    uint pc_prev = 0, pc_new = 0;

    struct instruction
    {
        uint rv32i, pc;
        bool isissue = 1;

        instruction(uint _rv32i = 0, uint _pc = 0, bool _isissue = 1) 
            : rv32i(_rv32i), pc(_pc), isissue(_isissue) {}
    } ins;

    struct Memory
    {
        unsigned char mem[500010];
    } M;

    struct Registers
    {
        uint reg[32];
        int reg_state[32];

        Registers() {memset(reg_state, -1, sizeof(reg_state));}

    } R_new, R_prev;

    struct Reorder_Buffer
    {
        struct node
        {
            int opcode, func3, func7, dest = -1, pc = -1, pos = 0;
            uint val = 0;
            bool ready = 0, isjump = 0, iscommit = 0, end = 0;

            node(int _opcode = -1, int _func3 = -1, int _func7 = -1) 
                : opcode(_opcode), func3(_func3), func7(_func7) {}

            void print() const
            {
                puts("ROB node:");
                printf("opcode:%d func3:%d func7:%d dest:%d pc:%d\n", opcode, func3, func7, dest, pc);
                printf("val:%u ready:%d isjump:%d iscommit:%d end:%d\n", val, ready, isjump, iscommit, end);
                puts("");
            }
        };

        Cycle_Queue<node, CPU::queue_len> h;

    } ROB_new, ROB_prev;

    struct Reservation_Station
    {
        struct node
        {
            int opcode, func3, func7; 
            uint Vj = 0, Vk = 0, A = 0; int Qj = -1, Qk = -1, dest = -1, pc = 0;

            node(int _opcode = -1, int _func3 = -1, int _func7 = -1) 
                : opcode(_opcode), func3(_func3), func7(_func7) {}

            void print() const
            {
                puts("ROB node:");
                printf("opcode:%d func3:%d func7:%d dest:%d pc:%d\n", opcode, func3, func7, dest, pc);
                printf("Vj:%u Vk:%u A:%u Qj:%d Qk:%d\n", Vj, Vk, A, Qj, Qk);
                puts("");
            }
        };

        Array<node, CPU::queue_len> h;

    } RS_new, RS_prev;

    struct Load_Store_Buffer
    {
        struct node
        {
            int opcode, func3, func7; 
            uint Vj = 0, Vk = 0, A = 0; int Qj = -1, Qk = -1, tim = -1, dest = -1, pos = 0;
            bool iscommit = 0;

            node(int _opcode = -1, int _func3 = -1, int _func7 = -1) 
                : opcode(_opcode), func3(_func3), func7(_func7) {}

            void print() const
            {
                puts("ROB node:");
                printf("opcode:%d func3:%d func7:%d\n", opcode, func3, func7);
                printf("Vj:%u Vk:%u A:%u Qj:%d Qk:%d tim:%d iscommit:%d\n", Vj, Vk, A, Qj, Qk, tim, iscommit);
                puts("");
            }
        };

        Cycle_Queue<node, CPU::queue_len> h;

    } LSB_new, LSB_prev;

    struct CDB
    {
        int dest; uint val; int pos; bool isjump = 0;
        CDB(int _dest = -1, uint _val = 0, int _pos = -1, bool _isjump = 0) 
            : dest(_dest), val(_val), pos(_pos), isjump(_isjump) {}

        void print() const
        {
            printf("dest:%d val:%u pos:%d isjump:%d\n", dest, val, pos, isjump);
        }
    };

    Reorder_Buffer::node issue_to_ROB, ROB_to_commit;
    Reservation_Station::node issue_to_RS, RS_to_ex;
    Load_Store_Buffer::node issue_to_LSB;

    CDB ex_to_CDB;
    CDB LSB_to_CDB_prev, LSB_to_CDB_new;
    CDB commit_to_reg;
    bool commit_to_LSB = 0;
    int commit_signal = 0; // 0 表示无    1 表示 pop   负数表示 pc

    std :: pair <int, int> issue_to_reg = std :: make_pair(-1, -1);

    // pc = -2 表示 Store Ready pc != -1 表示是 jump 操作

    void IQ_fetch();
    void IQ_issue();

    bool RS_ready(const CPU::Reservation_Station::node &t);
    int RS_find();
    void RS_run();
    void RS_ex();

    void ROB_run();
    void ROB_commit();

    void LSB_run();

    void reg_run();

    void update()
    {
        #ifdef func
            puts("");
            puts("---update---");
        #endif 
        R_prev = R_new; 
        pc_prev = pc_new;
        ROB_prev = ROB_new; 
        RS_prev = RS_new; 
        LSB_prev = LSB_new;

        issue_to_ROB = Reorder_Buffer::node();
        issue_to_RS = Reservation_Station::node();
        issue_to_LSB = Load_Store_Buffer::node();
        issue_to_reg = std::make_pair(-1, -1);

        LSB_to_CDB_prev = LSB_to_CDB_new;
        LSB_to_CDB_new = CDB();
        ex_to_CDB = CDB();

        commit_to_LSB = 0;
        commit_signal = 0;
        commit_to_reg = CDB();
        #ifdef func
            puts("");
            puts("------------");
        #endif 
    }

public:  

    void reader()
    {
        uint now = 0;
        char s[10];
        while (scanf("%s", s) != EOF)
        {
            if (s[0] == '@')
            {
                now = 0;
                for (int i = 1; i <= 8; i++)
                {
                    now = now << 4 | hex_to_dec(s[i]);
                }
            }
            else
            {
                M.mem[now++] = hex_to_dec(s[0]) << 4 | hex_to_dec(s[1]);
            }
        }
    }

    void runner()
    {
        int p[5];
        for (int i = 0; i < 5; i++)
            p[i] = i;
        while (1)
        {
            std :: random_shuffle(p, p + 5);
            for (int i = 0; i < 5; i++)
            {
                if (p[i] == 0)
                {
                    ROB_run();
                    if (ROB_to_commit.end)
                    {
                        std::cout << std::dec << ((unsigned int)R_prev.reg[10] & 255u);
                        return;
                    }
                }
                else if (p[i] == 1)
                {
                    LSB_run();
                }
                else if (p[i] == 2)
                {
                    RS_run();
                }
                else if (p[i] == 3)
                {
                    IQ_fetch();
                }
                else 
                {
                    reg_run();
                }
            }

            update();
            
            RS_ex();
            IQ_issue();
            ROB_commit();
        }
    }
};


#include "Instruction_Queue.hpp"
#include "Reorder_Buffer.hpp"
#include "Reservation_Station.hpp"
#include "Registers.hpp"
#include "Load_Store_Buffer.hpp"
#endif